<?php
	/* Functions in this file */
	/**************************/

	// ClearFlag($flag)
	// GetBasicBeat()
	// GetBeamingPatternAswxUint32()
	// GetBeatAmount()
	// GetBeatsPerMeasure()
	// GetMeasureTotal()
	// GetMFCClassName()
	// GetMFCClassSchema()
	// GetPulses()
	// GetText($type)
	// Hide()
	// IsCommonTime()
	// IsCompoundTime()
	// IsCutTime()
	// IsFlagSet($flag)
	// IsQuadrupleTime()
	// IsShown()
	// IsValidBeatAmount($beatAmount)
	// IsValidBeatsPerMeasure($beatsPerMeasure)
	// IsValidFlag($flag)
	// IsValidPulses($pulses)
	// SetBeamingPatternFromwxUint32($beamingPattern)
	// SetBeatAmount($beatAmount)
	// SetBeatsPerMeasure($beatsPerMeasure)
	// SetCommonTime()
	// SetCutTime()
	// SetFlag($flag)
	// SetPulses($pulses)
	// Show()
?>
<?php
	 
	 
	class TimeSignature {
		 
		 
		// Note: The MIDI_xxx duration constants found in this class are defined in generalmidi
		 
		const DEFAULT_DATA = 0x1a018000;
		const DEFAULT_PULSES = 4;
		 
		const MIN_BEATSPERMEASURE = 1;
		const MAX_BEATSPERMEASURE = 32;
		 
		const MIN_BEATAMOUNT = 2;
		const MAX_BEATAMOUNT = 32;
		 
		const MIN_PULSES = 0;
		const MAX_PULSES = 32;
		 
		 
		const meterMask = 0xff000000;
		const beatsPerMeasureMask = 0xf8000000;
		const beatAmountMask = 0x07000000;
		const beamingPatternMask = 0x000fffff;
		const beamingPatternBeat1Mask = 0x000f8000;
		const beamingPatternBeat2Mask = 0x00007c00;
		const beamingPatternBeat3Mask = 0x000003e0;
		const beamingPatternBeat4Mask = 0x0000001f;
		const show = 0x00100000;
		const brackets = 0x00200000;
		const commonTime = 0x00400000;
		const cutTime = 0x00800000;
		 
		 
		 
		const textBeatsPerMeasure = 0;
		const textBeatAmount = 1;
		const textFull = 2;
		 
		protected $m_data = self::DEFAULT_DATA;
		protected $m_pulses = self::DEFAULT_PULSES;
		 
		// Constructor/Destructor
		 
		 
		// MFC Class Functions
		 
		 
		function GetMFCClassName() {
			return "CTimeSignature2";
		}
		 
		 
		function GetMFCClassSchema() {
			return 1;
		}
		 
		 
		function IsSameMeter(&$timeSignature) {
			return (($this->GetBeatsPerMeasure() == $timeSignature->GetBeatsPerMeasure()) && ($this->GetBeatAmount() == $timeSignature->GetBeatAmount()));
		}
		// Sets the time signature to use the cut time symbol and meter
		function SetCutTime() {
			$this->SetMeter(2, 2);
			$this->SetFlag(self::cutTime); /* note: Flag must be set after call to $this->SetMeter, cause it clears both cut and common $flags) */
		}
		 
		 
		function IsCutTime() {
			return ($this->IsFlagSet(self::cutTime));
		}
		 
		function SetCommonTime() {
			$this->SetMeter(4, 4);
			$this->SetFlag(self::commonTime); /* see note above */
		}
		 
		 
		function IsCommonTime() {
			return ($this->IsFlagSet(self::commonTime));
		}
		 
		// Beats Per Measure Functions
		 
		 
		 
		function IsValidBeatsPerMeasure($beatsPerMeasure) {
			return (($beatsPerMeasure >= self::MIN_BEATSPERMEASURE) && ($beatsPerMeasure <= self::MAX_BEATSPERMEASURE));
		}
		 
		// Beat Amount Functions
		 
		 
		 
		function IsValidBeatAmount($beatAmount) {
			return (($beatAmount == 2) || ($beatAmount == 4) || ($beatAmount == 8) || ($beatAmount == 16) || ($beatAmount == 32));
		}
		 
		// Beaming Pattern Functions
		 
		 
		function IsValidBeamingPatternBeat($beat, $beat1) {
			return (($beat <= self::MAX_BEATAMOUNT) && (($beat1) ? ($beat > 0) : 1));
		}
		 
		// $this->Show Functions
		 
		function Show() {
			$this->SetFlag(self::show);
		}
		 
		function Hide() {
			$this->ClearFlag(self::show);
		}
		 
		 
		function IsShown() {
			return ($this->IsFlagSet(self::show));
		}
		 
		// Pulse Functions
		 
		 
		 
		function IsValidPulses($pulses) {
			return (($pulses >= self::MIN_PULSES) && ($pulses <= self::MAX_PULSES));
		}
		 
		 
		 
		function SetPulses($pulses) {
			if (!$this->IsValidPulses($pulses)) return 0;
			$this->m_pulses = $pulses;
			 return 1;
		}
		 
		 
		function GetPulses() {
			return ($this->m_pulses);
		}
		 
		// Flag Functions
		 
		 
		 
		 
		function IsValidFlag($flag) {
			return (($flag >= self::show) && ($flag <= (self::show | self::brackets | self::commonTime | self::cutTime)));
		}
		 
		 
		function ClearFlag($flag) {
			if (!$this->IsValidFlag($flag)) return 0;
			$this->m_data &= ~$flag;
			 return 1;
		}
		 
		 
		 
		function IsFlagSet($flag) {
			if (!$this->IsValidFlag($flag)) return 0;
			 return (($this->m_data & $flag) == $flag);
		}
		 
		 
		function TimeSignature($beatsPerMeasure = NULL, $beatAmount = NULL) {
			 
			//------Last Checked------//
			// - Dec 13, 2004
			$this->SetMeter($beatsPerMeasure, $beatAmount);
		}
		 
		
		function DoSerialize(&$stream){
		 
		
			WRITE_INTEGER ($stream,$this->m_data);
			WRITE_BYTE ($stream,$this->m_pulses);
		
			return 1;
		}
		 
		 
		 
		function DoDeserialize(&$stream){
		 
		
			$this->m_data = READ_INTEGER ($stream);
			$this->m_pulses = READ_BYTE ($stream);
		
		}
		
		// Meter Functions
		 
		 
		 
		 
		function SetMeter($beatsPerMeasure, $beatAmount) {
			 
			//------Last Checked------//
			// - Dec 13, 2004
			if (!$this->SetBeatsPerMeasure($beatsPerMeasure))
				return 0;
			if (!$this->SetBeatAmount($beatAmount))
				return 0;
			 
			// Set the default $beaming pattern, based on the meter's $beat amount and basic $beat
			$beatAmount = $this->GetBeatAmount();
			$basicBeat = $this->GetBasicBeat();
			 
			// Calculate the number of 8th notes in basic $beat
			if ($beatAmount <= 8) {
				 
				// 4/4 Time, group in 4s
				if (($beatAmount == 4) && ($this->GetBeatsPerMeasure() == 4))
					$this->SetBeamingPattern(4);
				else
					$this->SetBeamingPattern(($basicBeat / (MIDI_PPQN / 2)));
			}
			// Calculate the number of 16th notes in basic $beat
			elseif ($beatAmount == 16)
			$this->SetBeamingPattern(($basicBeat / (MIDI_PPQN / 4)));
			// Calculate the number of 32nd notes in basic $beat
			elseif ($beatAmount == 32)
			$this->SetBeamingPattern(($basicBeat / (MIDI_PPQN / 8)));
			 
			// Set the default $pulses
			$this->SetPulses(($this->GetMeasureTotal() / $basicBeat));
			 
			return 1;
		}
		 
		 
		 
		 
		function GetMeter(&$beatsPerMeasure, &$beatAmount) {
			 
			//------Last Checked------//
			// - Dec 12, 2004
			$beatsPerMeasure = $this->GetBeatsPerMeasure();
			$beatAmount = $this->GetBeatAmount();
		}
		 
		 
		 
		function IsCompoundTime() {
			 
			//------Last Checked------//
			// - Dec 12, 2004
			$beatsPerMeasure;
			$beatAmount;
			$this->GetMeter($beatsPerMeasure, $beatAmount);
			return ((($beatsPerMeasure % 3) == 0) && ($beatsPerMeasure != 3));
		}
		 
		 
		 
		function IsQuadrupleTime() {
			 
			//------Last Checked------//
			// - Dec 12, 2004
			$beatsPerMeasure;
			$beatAmount;
			$this->GetMeter($beatsPerMeasure, $beatAmount);
			return (($beatsPerMeasure == 4) || ($beatsPerMeasure == 12));
		}
		 
		 
		 
		function GetMeasureTotal() {
			 
			//------Last Checked------//
			// - Dec 12, 2004
			return (((MIDI_PPQN * 4) / $this->GetBeatAmount()) * $this->GetBeatsPerMeasure());
		}
		 
		 
		 
		function GetBasicBeat() {
			 
			//------Last Checked------//
			// - Dec 12, 2004
			 
			// Compound time = dotted $beat
			if ($this->IsCompoundTime())
				return ((MIDI_PPQN * 4) * 3 / $this->GetBeatAmount());
			return ((MIDI_PPQN * 4) / $this->GetBeatAmount());
		}
		 
		// Beats Per Measure Functions
		 
		 
		 
		function SetBeatsPerMeasure($beatsPerMeasure) {
			 
			//------Last Checked------//
			// - Dec 13, 2004
			if (!$this->IsValidBeatsPerMeasure($beatsPerMeasure)) return 0;
			 
			// Automatically clear the common and cut time $flags
			$this->ClearFlag(self::commonTime | self::cutTime);
			 
			// Beats per measure are zero-based in storage
			$beatsPerMeasure--;
			 
			$this->m_data &= ~self::beatsPerMeasureMask;
			$this->m_data |= ($beatsPerMeasure << 27);
			return 1;
		}
		 
		 
		 
		function GetBeatsPerMeasure() {
			 
			//------Last Checked------//
			// - Dec 12, 2004
			 
			// Common Time
			if ($this->IsCommonTime())
				return (4);
			// Cut Time
			elseif ($this->IsCutTime())
			return (2);
			 
			// $beatsPerMeasure is stored in zero-based format, so add one
			$beatsPerMeasure = (($this->m_data & self::beatsPerMeasureMask) >> 27);
			$beatsPerMeasure++;
			return ($beatsPerMeasure);
		}
		 
		// Beat Amount Functions
		 
		 
		 
		function SetBeatAmount($beatAmount) {
			 
			//------Last Checked------//
			// - Dec 13, 2004
			if (!$this->IsValidBeatAmount($beatAmount)) return 0;
			 
			// Automatically clear the common and cut time $flags
			$this->ClearFlag(self::commonTime | self::cutTime);
			 
			// The $beat amount is stored in power of 2 form
			if ($beatAmount == 2)
				$beatAmount = 1;
			elseif ($beatAmount == 4)
			$beatAmount = 2;
			elseif ($beatAmount == 8)
			$beatAmount = 3;
			elseif ($beatAmount == 16)
			$beatAmount = 4;
			elseif ($beatAmount == 32)
			$beatAmount = 5;
			 
			$this->m_data &= ~self::beatAmountMask;
			$this->m_data |= ($beatAmount << 24);
			return 1;
		}
		 
		 
		 
		function GetBeatAmount() {
			 
			//------Last Checked------//
			// - Dec 13, 2004
			 
			// Common Time
			if ($this->IsCommonTime())
				return (4);
			// Cut Time
			elseif ($this->IsCutTime())
			return (2);
			 
			// The $beat amount is stored in power of 2 form
			$beatAmount = (($this->m_data & self::beatAmountMask) >> 24);
			$beatAmount = (pow(2, $beatAmount));
			return ($beatAmount);
		}
		 
		// Beaming Pattern Functions
		// Sets the $beaming pattern
		 
		 
		 
		 
		 
		function SetBeamingPattern($beat1, $beat2, $beat3, $beat4) {
			 
			//------Last Checked------//
			// - Dec 13, 2004
			if (!$this->IsValidBeamingPatternBeat($beat1, true)) return 0;
			if (!$this->IsValidBeamingPatternBeat($beat2, false)) return 0;
			if (!$this->IsValidBeamingPatternBeat($beat3, false)) return 0;
			if (!$this->IsValidBeamingPatternBeat($beat4, false)) return 0;
			 
			// Clear the old beam data
			$this->m_data &= ~self::beamingPatternMask;
			 
			// Data is stored in zero-based format
			if ($beat1 > 0)
				$beat1--;
			 
			if ($beat2 > 0)
				$beat2--;
			 
			if ($beat3 > 0)
				$beat3--;
			 
			if ($beat4 > 0)
				$beat4--;
			 
			// Construct the $beaming data
			$beaming = 0;
			 
			$beaming = $beat1;
			$beaming <<= 5;
			$beaming |= $beat2;
			$beaming <<= 5;
			$beaming |= $beat3;
			$beaming <<= 5;
			$beaming |= $beat4;
			 
			// Update the $beaming pattern
			$this->m_data |= $beaming;
			 
			return 1;
		}
		 
		 
		 
		 
		 
		 
		function GetBeamingPattern(&$beat1, &$beat2, &$beat3, &$beat4) {
			 
			//------Last Checked------//
			// - Dec 13, 2004
			 
			// All the beam data is stored in zero-based format
			$beat1 = (($this->m_data & self::beamingPatternBeat1Mask) >> 15);
			$beat1++;
			 
			$beat2 = (($this->m_data & self::beamingPatternBeat2Mask) >> 10);
			if ($beat2 > 0)
				$beat2++;
			 
			$beat3 = (($this->m_data & self::beamingPatternBeat3Mask) >> 5);
			if ($beat3 > 0)
				$beat3++;
			 
			$beat4 = ($this->m_data & self::beamingPatternBeat4Mask);
			if ($beat4 > 0)
				$beat4++;
		}
		 
		 
		 
		 
		function SetBeamingPatternFromwxUint32($beamingPattern) {
			 
			//------Last Checked------//
			// - Dec 13, 2004
			return ($this->SetBeamingPattern(
			LOBYTE(LOWORD($beamingPattern)),
				HIBYTE(LOWORD($beamingPattern)),
				LOBYTE(HIWORD($beamingPattern)),
				HIBYTE(HIWORD($beamingPattern))
			)
			);
		}
		 
		 
		 
		function GetBeamingPatternAswxUint32() {
			 
			//------Last Checked------//
			// - Dec 12, 2004
			$beat1;
			$beat2;
			$beat3;
			$beat4;
			$this->GetBeamingPattern($beat1, $beat2, $beat3, $beat4);
			return (MAKELONG(MAKEWORD($beat1, $beat2), MAKEWORD($beat3, $beat4)));
		}
		 
		// Flag Functions
		 
		 
		function SetFlag($flag) {
			 
			//------Last Checked------//
			// - Dec 13, 2004
			if (!$this->IsValidFlag($flag)) return 0;
			 
			// Perform any mutual exclusive $flag clearing
			if (($flag & self::commonTime) == self::commonTime)
				$this->ClearFlag(self::cutTime);
			if (($flag & self::cutTime) == self::cutTime)
				$this->ClearFlag(self::commonTime);
			$this->m_data |= $flag;
			return 1;
		}
		 
		// Text Functions
		 
		 
		 
		function GetText($type) {
			 
			$beatsPerMeasure = $this->GetBeatsPerMeasure();
			$beatAmount = $this->GetBeatAmount();
			 
			$returnValue = "";
			 
			// Beats per measure only
			if ($type == self::textBeatsPerMeasure)
				$returnValue = sprintf("%d", $beatsPerMeasure);
			// Beat amount only
			elseif ($type == self::textBeatAmount)
			$returnValue = sprintf("%d", $beatAmount);
			// Full meter
			else
				$returnValue = sprintf("%d/%d", $beatsPerMeasure, $beatAmount);
			 
			return $returnValue;
			 
		}
	}
	 
	 
?>

